package javacompiler.syntax;

import static org.junit.Assert.fail;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

import javacompiler.lex.JavaLexicalAnalyser;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class JavaSyntaxAnalyserTest {

	private String testFileName = "test2.txt";
	
	private JavaSyntaxAnalyser syntaxAnalyser;
	private JavaLexicalAnalyser lexicalAnalyser;
	
	@Before
	public void setUp() throws IOException {
		File testFile = new File(testFileName);
		testFile.createNewFile();
		
		lexicalAnalyser = new JavaLexicalAnalyser(
						new BufferedInputStream(new FileInputStream(testFile)));
		syntaxAnalyser = new JavaSyntaxAnalyser(lexicalAnalyser);
	}
	
	@After
	public void tearDown() {
		new File(testFileName).delete();
		SymbolsTable.reset();
		JavaSyntaxAnalyser.table = SymbolsTable.getInstance();
		IdGenerator.reset();
		JavaSyntaxAnalyser.generator = IdGenerator.getInstance();
	}
	
	private void expectException() {
		try {
			syntaxAnalyser.parse();
			fail("Expected an exception.");
		} catch (Exception e) {
			// success
		}
	}
	
	/*
	 *  
	 *  IMPORT RULES
	 * 
	 */
	
	@Test
	public void testImports() throws Exception {
		writeCorrectImports(testFileName);
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncompleteImports1();
		
		try {
			syntaxAnalyser.parse();
			fail("Expected an exception.");
		} catch (Exception e) {
			// success
		}
		
		tearDown();
		setUp();
		
		writeIncompleteImports2();
		
		try {
			syntaxAnalyser.parse();
			fail("Expected an exception.");
		} catch (Exception e) {
			// success
		}
	}
	
	private void writeCorrectImports(String testFileName) throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("import class1;");
		f.writeBytes("import package1.package2.class1;\n");
		f.writeBytes("import package1.class1;");
		
		f.close();
	}

	private void writeIncompleteImports1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("import class1\n");
		f.writeBytes("\n");
		f.writeBytes("import package1.class1;");
		
		f.close();
	}
	
	private void writeIncompleteImports2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("import class1;\n");
		f.writeBytes("\n");
		f.writeBytes("package1.class1;");
		
		f.close();
	}
	
	/*
	 * 
	 * PACKAGE RULES
	 * 
	 */
	
	@Test
	public void testPackage() throws Exception {
		writeCorrectPackages();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncompletePackage1();
		expectException();
		
		tearDown();
		setUp();
		
		writeIncompletePackage2();
		expectException();
		
		tearDown();
		setUp();
		
		writeDuplicatedPackage();
		expectException();
	}

	private void writeCorrectPackages() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("package class1;\n");
		f.writeBytes("\n");
		
		f.close();
	}
	
	private void writeIncompletePackage1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("package class1\n");
		f.writeBytes("\n");
		
		f.close();
	}
		
	private void writeIncompletePackage2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("class1;\n");
		f.writeBytes("\n");
		
		f.close();
	}
	
	private void writeDuplicatedPackage() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("package class1;\n");
		f.writeBytes("package class2;\n");
		f.writeBytes("\n");
		
		f.close();
	}
	
	/*
	 * 
	 * INTERFACE RULES
	 * 
	 */
	
	@Test
	public void testInterfaceDeclaration() throws Exception { 
		writeCorrectPublicInterfaceDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectPackageInterfaceDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeInterfaceInheritance();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeInterfaceMultiInheritance();
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeInterfaceImplements();
		expectException();
		
		tearDown();
		setUp();
		
		writeIncompleteInterfaceDeclaration();
		expectException();
	}

	private void writeCorrectPublicInterfaceDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public interface Interface1 { }");
		
		f.close();
	}
	
	private void writeCorrectPackageInterfaceDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("interface Interface1 { }");
		
		f.close();
	}
	
	private void writeInterfaceInheritance() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("interface Interface1 extends Interface2 { }");
		
		f.close();
	}
	
	private void writeInterfaceMultiInheritance() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("interface Interface1 extends Interface2, Interface3, Interface4 { }");
		
		f.close();
	}
	
	private void writeInterfaceImplements() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("interface Interface1 implements Interface2 { }");
		
		f.close();
	}
	
	private void writeIncompleteInterfaceDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("Interface1 { }");
		
		f.close();
	}
	
	/*
	 * 
	 * INTERFACE FIELD DECLARATION
	 * 
	 */
	
	@Test
	public void testInterfaceFieldDeclaration() throws Exception {
		writeCorrectMethodDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncompleteMethodDeclaration();
		expectException();
		
		tearDown();
		setUp();
		
		writeMisplacedMethodDeclaration1();
		expectException();
		
		tearDown();
		setUp();
		
		writeMisplacedMethodDeclaration2();
		expectException();
	}

	private void writeCorrectMethodDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("interface Interface1 { int method1(); public float method2(int a, float b); protected String method3(String b); }");
		
		f.close();
	}

	private void writeIncompleteMethodDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("interface Interface1 { int (); public float method2(int a, float b); protected String method3(String b); }");
		
		f.close();
	}

	private void writeMisplacedMethodDeclaration1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("interface Interface1 { int method1(); public float method2(int a, float b); } protected String method3(String b);");
		
		f.close();
	}
	
	private void writeMisplacedMethodDeclaration2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("protected String method3(String b); interface Interface1 { int method1(); public float method2(int a, float b); }");
		
		f.close();
	}
	
	/*
	 * 
	 * CLASS RULES
	 * 
	 */
	
	@Test
	public void testClassDeclaration() throws Exception {
		writeCorrectPublicClassDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectProtectedClassDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectPackageClassDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectStaticClassDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectAbstractClassDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncompleteClassDeclaration();
		expectException();
	}

	private void writeCorrectPublicClassDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { }");
		
		f.close();
	}
	
	private void writeCorrectProtectedClassDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("protected class Class1 { }");
		
		f.close();
	}
	
	private void writeCorrectPackageClassDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("class Class1 { }");
		
		f.close();
	}
	
	private void writeCorrectStaticClassDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public static class Class1 { }");
		
		f.close();
	}
	
	private void writeCorrectAbstractClassDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public abstract class Class1 { }");
		
		f.close();
	}
	
	private void writeIncompleteClassDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public Class1 { }");
		
		f.close();
	}
	
	/*
	 * 
	 * CLASS FIELD AND VARIABLES DECLARATIONS
	 * 
	 */
	
	@Test
	public void testClassFieldDeclaration() throws Exception {
		writeCorrectClassMethods();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectClassVariableDeclaration();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeMixingCorrectClassMethodsAndVariables();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncorrectClassMethods1();
		expectException();
		
		tearDown();
		setUp();
		
		writeIncorrectClassMethods2();
		expectException();
		
		tearDown();
		setUp();
		
		writeIncorrectClassMethods3();
		expectException();
		
		tearDown();
		setUp();
		
		writeIncorrectClassVariables1();
		expectException();
		
		tearDown();
		setUp();
		
		writeIncorrectClassVariables2();
		expectException();
		
		tearDown();
		setUp();
		
		writeArrayDeclarationAndInitialization();
		
		syntaxAnalyser.parse();
	}

	private void writeCorrectClassMethods() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { public int method1() {} protected String method2() {} }");
		
		f.close();
	}

	private void writeCorrectClassVariableDeclaration() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;}");
		
		f.close();
	}

	private void writeMixingCorrectClassMethodsAndVariables() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1() {} protected String method2() {} }");
		
		f.close();
	}
	
	private void writeIncorrectClassMethods1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1() {} protected String () {} }");
		
		f.close();
	}
	
	private void writeIncorrectClassMethods2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1() protected String method2() {} }");
		
		f.close();
	}
	
	private void writeIncorrectClassMethods3() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("method1() {} protected String method2() {} }");
		
		f.close();
	}
	
	private void writeIncorrectClassVariables1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int; protected float t; private String string1;");
		f.writeBytes("public int method1() {} protected String method2() {} }");
		
		f.close();
	}
	
	private void writeIncorrectClassVariables2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t private String string1;");
		f.writeBytes("public int method3() {} protected String method2() {} }");
		
		f.close();
	}
	
	private void writeArrayDeclarationAndInitialization() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { public int[] array = new int[]; public int method1() {} protected String method2() {} }");
		
		f.close();
	}
	
	/*
	 * 
	 * IF BLOCKS
	 * 
	 */
	
	@Test
	public void testIFblocks() throws Exception {
		writeCorrectIfOnlyBlock();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectSimpleIfElseBlock();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectMultiIfElseBlock();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncorrectIfConditionDelimiter();
		expectException();
		
		tearDown();
		setUp();
		
		writeEmptyIfCondition();
		expectException();
		
		tearDown();
		setUp();
		
		writeElseWithoutIf();
		expectException();
		
		tearDown();
		setUp();
		
		writeMultiElse();
		expectException();
		
		tearDown();
		setUp();
		
		writeMultiStatementIfElse();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIfWithNoBooleanCondition1();
		expectException();
		
		tearDown();
		setUp();
		
		writeIfWithNoBooleanCondition2();
		expectException();
		
		tearDown();
		setUp();
		
		writeStatementBetweenIfAndElse();
		expectException();
	}

	private void writeCorrectIfOnlyBlock() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private static int x; protected float t; \n");
		f.writeBytes("public int method1(int x, int y) { if ( true && true ) return 2; return x; } }");
		
		f.close();
	}
	
	private void writeCorrectSimpleIfElseBlock() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { if ( x < 10 ) return 2; else return 3; } }");
		
		f.close();
	}
	
	private void writeCorrectMultiIfElseBlock() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { if (true) return 2; else if ( 1 == 2 ) return 3; else return 0; } }");
		
		f.close();
	}
	
	private void writeIncorrectIfConditionDelimiter() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { if  true ) return 2; else return 3; } }");
		
		f.close();
	}
	
	private void writeEmptyIfCondition() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t = 1.1, z[][]; private String string1 = \"string\";");
		f.writeBytes("public int method1(int x) { if ( ) return 2; else return 3; } }");
		
		f.close();
	}
	
	private void writeElseWithoutIf() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t = 1.1, z[][]; private String string1 = \"string\";");
		f.writeBytes("public int method1(int x) { else return 3; } }");
		
		f.close();
	}
	
	private void writeMultiElse() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t = 1.1, z[][]; private String string1 = \"string\";");
		f.writeBytes("public int method1(int x) { if (true) return 2; else return 3; else return 0; } }");
		
		f.close();
	}
	
	private void writeMultiStatementIfElse() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { if (true) { throw a; return 2; } else { int b; return 3; } } }");
		
		f.close();
	}

	private void writeIfWithNoBooleanCondition1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { if ( x ) { throw a; return 2; } else { int b; return 3; } } }");
		
		f.close();
	}

	private void writeIfWithNoBooleanCondition2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { if ( 1 ) { throw a; return 2; } else { int b; return 3; } } }");
		
		f.close();
	}
	
	private void writeStatementBetweenIfAndElse() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { if ( x < 10) { throw a; return 2; } return 3; else { int b; return 3; } } }");
		
		f.close();
	}
	
	/*
	 * 
	 * WHILE BLOCKS
	 * 
	 */
	
	@Test
	public void testWhileBlock() throws Exception {
		writeCorrectWhileBlock();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeWhileBlockEmptyCondition();
		expectException();
		
		tearDown();
		setUp();
		
		writeWhileBlockNoBody();
		expectException();
		
		tearDown();
		setUp();
		
		writeWhileBlockOneCommandBody();
		
		syntaxAnalyser.parse();
	}

	private void writeCorrectWhileBlock() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { while ( x < 10 ) { x = 2 + 2; } } }");
		
		f.close();
	}
	
	private void writeWhileBlockEmptyCondition() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { while () { x++; } } }");
		
		f.close();
	}

	private void writeWhileBlockNoBody() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { while ( x < 10) } }");
		
		f.close();
	}
	
	private void writeWhileBlockOneCommandBody() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int method1(int x) { while ( x < 10) x++; } }");
		
		f.close();
	}
	
	/*
	 * 
	 * ASSIGNMENTS
	 * 
	 */
	
	@Test
	public void testAssignments() throws Exception {
		writeCorrectAssignment();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncompleteAssignment1();
		expectException();
		
		tearDown();
		setUp();
		
		writeIncompleteAssignment2();
		expectException();
		
		tearDown();
		setUp();
		
		writeAssignmentOfDifferentTypes1();
		expectException();
		
		tearDown();
		setUp();
		
		writeAssignmentOfDifferentTypes2();
		expectException();
		
		tearDown();
		setUp();
		
		writeAssignmentOfDifferentTypes3();
		expectException();
		
		tearDown();
		setUp();
		
		writeFunctionResultAssignment();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeIncorrectFunctionResultAssignment();
		expectException();
	}

	private void writeCorrectAssignment() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private static int x; protected double t; private String string1;");
		f.writeBytes("public int setX(int newX) { x = newX; } protected int setT(int newT) { t = 1.1; }}");
		
		f.close();
	}
	
	private void writeIncompleteAssignment1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t = 1.1, z[][]; private String string1 = \"string\";");
		f.writeBytes("public int setX(int newX) { x = newX } protected int setT(int newT) { t = 2*newT + 1; }}");
		
		f.close();
	}
	
	private void writeIncompleteAssignment2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t = 1.1, z[][]; private String string1 = \"string\";");
		f.writeBytes("public int setX(int newX) { x = ; } protected int setT(int newT) { t = 2*newT + 1; }}");
		
		f.close();
	}
	
	private void writeAssignmentOfDifferentTypes1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int x; private int y; public int setX(int newX) { x = \"blablabla\"; } }");
		
		f.close();
	}
	
	private void writeAssignmentOfDifferentTypes2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private Type1 x; private int y; public int setX(int newX) { x = \"blablabla\"; } }");
		
		f.close();
	}
	
	private void writeAssignmentOfDifferentTypes3() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int x; private int y; private Class1 t; public int setX(int newX) { x = true; } }");
		
		f.close();
	}
	
	private void writeFunctionResultAssignment() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int x; private int y; private Class1 t; public int setX(int newX) { x = 1; }  ");
		f.writeBytes("  public int setY(int newX) { x = setX(newX); } } ");
		
		f.close();
	}
	
	private void writeIncorrectFunctionResultAssignment() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int x; private int y; private Class1 t; public float setX(int newX) { x = 1; }  ");
		f.writeBytes("  public int setY(int newX) { x = setX(newX); } } ");
		
		f.close();
	}
	
	@Test
	public void testRelationalCommands() throws Exception {
		writeCorrectRelationalCommands();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeNumericRelationalCommandWithNonNumericOperands();
		expectException();
		
		tearDown();
		setUp();
		
		writeRelationalCommandWithNonNumericOperands();
		
		syntaxAnalyser.parse();
	}

	private void writeCorrectRelationalCommands() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int x; private int y; private boolean t; public int setX(int newX) { t = x < 10; } }");
		
		f.close();
	}

	private void writeNumericRelationalCommandWithNonNumericOperands() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private boolean x; private int y; private boolean t; public int setX(int newX) { t = x < 10; } }");
		
		f.close();
	}
	
	private void writeRelationalCommandWithNonNumericOperands() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private boolean x; private int y; private boolean t; public int setX(int newX) { t = x == true; setX (y); } }");
		
		f.close();
	}
	
	@Test
	public void testOverloading() throws Exception {
		writeMethodDuplication();
		expectException();
		
		tearDown();
		setUp();
		
		writeCorrectOverloading1();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeCorrectOverloading2();
		
		syntaxAnalyser.parse();
		
		tearDown();
		setUp();
		
		writeDuplicatedMethodsWithDifferentParametersNames();
		expectException();
		
		tearDown();
		setUp();
		
		writeMethodsDifferentNamesAndSameParameters();
		
		syntaxAnalyser.parse();
	}

	private void writeMethodDuplication() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private boolean x; private int y; public int setX(int newX) { } public int setX(int newX) { }}");
		
		f.close();
	}

	private void writeCorrectOverloading1() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private boolean x; private int y; public int setX(int newX) { } public int setX(int newX, float y) { }}");
		
		f.close();
	}
	
	private void writeCorrectOverloading2() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private boolean x; private int y; public int setX(float y, int newX) { } public int setX(int newX, float y) { }}");
		
		f.close();
	}
	
	private void writeDuplicatedMethodsWithDifferentParametersNames() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private boolean x; private int y; public int setX(int x, float newY) { } public int setX(int newX, float y) { }}");
		
		f.close();
	}
	
	private void writeMethodsDifferentNamesAndSameParameters() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private boolean x; private int y; public int setX(int x, float newY) { } public int another(int newX, float y) { }}");
		
		f.close();
	}
	
	@Test
	public void testMethodCalls() throws Exception {
		writeIncorrectMethodCall();
		expectException();
		
		tearDown();
		setUp();
		
		writeRecursiveCall();
		syntaxAnalyser.parse();
		
		// FIXME not passing
		/*tearDown();
		setUp();
		
		writeIncorrectRecursiveCall();
		expectException();*/
	}

	private void writeIncorrectMethodCall() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int y; public int setX(int newX, float y) { ; } public int setY(int x) { setX( x ); }}");
		
		f.close();
	}

	private void writeRecursiveCall() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int y; private int [ ] array = new int [ ] ; public int setX(int newX, float y) { ; } public int setY(int x) { setY( x ); }}");
		
		f.close();
	}

	private void writeIncorrectRecursiveCall() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int y; private float t; public int setX(int newX, float y) { ; } public int setY(int x) { setY( t ); }}");
		
		f.close();
	}
	
	//@Test
	public void testAttributeDeclaration() throws IOException {
		// FIXME Not passing
		writeDuplicatedFieldDeclarationSameNamesAndTypes();
		expectException();
		
		tearDown();
		setUp();
		
		writeDuplicatedFieldDeclarationSameNamesDifferentTypes();
		expectException();
	}

	private void writeDuplicatedFieldDeclarationSameNamesAndTypes() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private int y; private int y; }");
		
		f.close();
	}

	private void writeDuplicatedFieldDeclarationSameNamesDifferentTypes() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");
		
		f.writeBytes("public class Class1 { private float y; private int y; }");
		
		f.close();
	}
	
	//@Test
	// FIXME not passing in the tests
	public void testInstantiation() throws Exception {
		writeCorrectInstantiation();
		
		syntaxAnalyser.parse();
	}

	private void writeCorrectInstantiation() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("public class Class1 { private static int x; protected float t; private String string1;");
		f.writeBytes("public int newObject(int newX) { string1 = new String[] ; } }");
		
		f.close();
	}
	
	@Test
	public void testCodeGeneration() throws Exception {
		writeBasicCode();

		syntaxAnalyser.parse();
	}

	private void writeBasicCode() throws IOException {
		RandomAccessFile f = new RandomAccessFile(new File(testFileName), "rw");

		f.writeBytes("import k; public class Class1 { private static int x; protected int t; private String string1; private boolean s;");
		f.writeBytes("private float b(int b) { 1 + 1; return; }  public int newObject(int newX) { if (newX == 1) { 1 + 3; } else { x = 1; } x = 10; newObject(t); b(t); } ");
		f.writeBytes("}");
		
		f.close();
	}
}
